package cn.lg.soar.common.util;

import cn.lg.soar.common.util.reflect.ReflectUtil;
import org.springframework.asm.ClassVisitor;
import org.springframework.asm.Type;
import org.springframework.cglib.core.*;
import org.springframework.util.ConcurrentReferenceHashMap;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.security.ProtectionDomain;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.stream.Stream;

/**
 * 属性复制工具（基于spring提供的BeanCopier源码改造）
 * @author luguoxiang
 * @date 2023/01/05
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public abstract class BeanCopier<S, T> {

    /**
     * 属性复制（类型不匹配的字段不会复制，注意泛型无法复制）
     * @param source 源数据
     * @param target 目标数据
     */
    public abstract void copy(S source, T target);

    /**
     * 转换为目标类型（类型不匹配的字段不会复制，注意泛型无法复制）
     * @param source 源数据
     * @return
     */
    public abstract T convert(S source);

    /**
     * 复制，数据可为null
     * @param source
     * @param target
     */
    public void copyNullable(S source, T target) {
        if (source == null || target == null) {
            return;
        }
        copy(source, target);
    }

    /**
     * 转换，数据可为null
     * @param source
     * @return
     */
    public T convertNullable(S source) {
        if (source == null) {
            return null;
        }
        return convert(source);
    }

    /**
     * 批量转换
     * @param source
     * @return
     */
    public Stream<T> convertBatch(Collection<? extends S> source) {
        return source.stream().map(this::convertNullable);
    }
    /**
     * 批量转换
     * @param source
     * @return
     */
    public Stream<T> convertBatch(S[] source) {
        return Arrays.stream(source).map(this::convertNullable);
    }
    /**
     * 批量转换
     * @param source 源数据
     * @param handler 处理器
     * @return
     */
    public Stream<T> convertBatch(Collection<? extends S> source, BiFunction<S, T, T> handler) {
        return source.stream().map(s -> {
            if (s == null) {
                return handler.apply(null, null);
            }
            return handler.apply(s, convert(s));
        });
    }
    /**
     * 批量转换
     * @param source 源数据
     * @param handler 处理器
     * @return
     */
    public Stream<T> convertBatch(S[] source, BiFunction<S, T, T> handler) {
        return Arrays.stream(source).map(s -> {
            if (s == null) {
                return handler.apply(null, null);
            }
            return handler.apply(s, convert(s));
        });
    }

    private static final SoarBeanCopierKey KEY_FACTORY = (SoarBeanCopierKey)KeyFactory.create(SoarBeanCopierKey.class);
    private static final Type BEAN_COPIER = TypeUtils.parseType("cn.lg.soar.common.util.BeanCopier");
    private static final Signature COPY =
            new Signature("copy", Type.VOID_TYPE, new Type[]{Constants.TYPE_OBJECT, Constants.TYPE_OBJECT});
    private static final Signature CONVERT =
            new Signature("convert", Constants.TYPE_OBJECT, new Type[]{Constants.TYPE_OBJECT});
    private static final Map<Class, Map<Class, BeanCopier>> CACHE1 =
            new ConcurrentReferenceHashMap<>(64);
    private static final Map<Class, Map<Class, Map<String, BeanCopier>>> CACHE2 =
            new ConcurrentReferenceHashMap<>(64);

    /**
     * 创建一个复制器，不带缓存
     * @param source 源数据类类型
     * @param target 目标数据类类型
     * @return
     * @param <S>
     * @param <T>
     */
    public static <S, T>BeanCopier<S, T> create(Class<S> source, Class<T> target) {
        return new Generator<>(source, target).create();
    }

    /**
     * 创建一个复制器，不带缓存
     * @param source 源数据类类型
     * @param target 目标数据类类型
     * @param ignoreFields 要忽略的字段“,”隔开，（忽略的字段不会复制）
     * @return
     * @param <S>
     * @param <T>
     */
    public static <S, T>BeanCopier<S, T> create(Class<S> source, Class<T> target, String ignoreFields) {
        return new Generator<>(source, target, ignoreFields).create();
    }

    /**
     * 创建一个复制器，带缓存
     * @param source 源数据类类型
     * @param target 目标数据类类型
     * @return
     * @param <S>
     * @param <T>
     */
    public static <S, T>BeanCopier<S, T> createCacheable(Class<S> source, Class<T> target) {
        return CACHE1.computeIfAbsent(source, k -> new HashMap<>())
                .computeIfAbsent(target, k -> create(source, target));
    }

    /**
     * 创建一个复制器，带缓存
     * @param source 源数据类类型
     * @param target 目标数据类类型
     * @param ignoreFields 要忽略的字段“,”隔开，（忽略的字段不会复制）
     * @return
     * @param <S>
     * @param <T>
     */
    public static <S, T>BeanCopier<S, T> createCacheable(Class<S> source, Class<T> target, String ignoreFields) {
        return CACHE2.computeIfAbsent(source, k -> new HashMap<>())
                .computeIfAbsent(target, k -> new HashMap<>())
                .computeIfAbsent(ignoreFields, k -> create(source, target, ignoreFields));
    }

    /**
     * 代理类构造器
     * @param <S>
     * @param <T>
     */
    public static class Generator<S, T> extends AbstractClassGenerator {
        private static final Source SOURCE = new Source(BeanCopier.class.getName());
        private Class<S> source;
        private Class<T> target;
        private String ignoreFields;

        public Generator(Class<S> source, Class<T> target) {
            super(SOURCE);
            this.setSource(source);
            this.setTarget(target);
        }

        public Generator(Class<S> source, Class<T> target, String ignoreFields) {
            super(SOURCE);
            this.ignoreFields = ignoreFields;
            this.setSource(source);
            this.setTarget(target);
        }

        public void setSource(Class<S> source) {
            if (!Modifier.isPublic(source.getModifiers())) {
                this.setNamePrefix(source.getName());
            }
            this.source = source;
        }

        public void setTarget(Class<T> target) {
            if (!Modifier.isPublic(target.getModifiers())) {
                this.setNamePrefix(target.getName());
            }

            this.target = target;
        }

        protected ClassLoader getDefaultClassLoader() {
            return this.source.getClassLoader();
        }

        protected ProtectionDomain getProtectionDomain() {
            return ReflectUtils.getProtectionDomain(this.source);
        }

        public BeanCopier<S, T> create() {
            Object key = BeanCopier.KEY_FACTORY.newInstance(this.source.getName(), this.target.getName(), ignoreFields);
            return (BeanCopier<S, T>)super.create(key);
        }

        /**
         * 生成代理类
         * @param v
         */
        public void generateClass(ClassVisitor v) {
            Type sourceType = Type.getType(this.source);
            Type targetType = Type.getType(this.target);
            ClassEmitter classEmitter = new ClassEmitter(v);
            classEmitter.begin_class(52, 1, this.getClassName(), BeanCopier.BEAN_COPIER, (Type[])null, "<generated>");
            EmitUtils.null_constructor(classEmitter);

            // 生成 copy 方法
            CodeEmitter emitter1 = classEmitter.begin_method(1, BeanCopier.COPY, (Type[])null);
            Local targetLocal = emitter1.make_local();
            Local sourceLocal = emitter1.make_local();
            emitter1.load_arg(1);
            emitter1.checkcast(targetType);
            emitter1.store_local(targetLocal);
            emitter1.load_arg(0);
            emitter1.checkcast(sourceType);
            emitter1.store_local(sourceLocal);
            // 扫描源数据的get方法
            Map<String, Method> getters = new HashMap<>(16);
            ReflectUtil.scanMethod(this.source, x -> {
                if (x.getName().startsWith("get") && x.getParameterCount() == 0) {
                    getters.put(x.getName(), x);
                }
            });

            Predicate<String> predicate;
            if (ignoreFields == null) {
                predicate = name -> false;
            } else {
                String[] ignores = ignoreFields.split(",");
                Set<String> ignoreSet = new HashSet<>(ignores.length);
                for (String s : ignores) {
                    ignoreSet.add(s);
                }
                predicate = name -> ignoreSet.contains(name.substring(3).toLowerCase());
            }

            // 扫描目标类的set方法，将匹配的字段使用get set进行复制
            ReflectUtil.scanMethod(this.target, setter -> {
                String name = setter.getName();
                if (name.startsWith("set") && setter.getParameterCount() == 1) {
                    // 过滤忽略字段
                    if (predicate.test(name)) {
                        return;
                    }
                    // 字段名匹配
                    Method getter = getters.get("g" + name.substring(1));
                    if (getter == null) {
                        return;
                    }
                    // 数据类型匹配
                    Class<?> parameterType = setter.getParameterTypes()[0];
                    Class<?> returnType = getter.getReturnType();
                    // 目标字段类型是源字段类型的父类或同类型的可以复制，反正不复制
                    if (!parameterType.isAssignableFrom(returnType)) {
                        return;
                    }
                    try {
                        // 泛型匹配，因不确定代码是否有异常，为保险加上了try catch
                        java.lang.reflect.Type genericParameterType = setter.getGenericParameterTypes()[0];
                        if (genericParameterType instanceof ParameterizedType) {
                            java.lang.reflect.Type genericReturnType = getter.getGenericReturnType();
                            if (!(genericReturnType instanceof ParameterizedType)) {
                                return;
                            }
                            java.lang.reflect.Type[] parameterTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                            java.lang.reflect.Type[] returnTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
                            if (parameterTypeArguments.length != returnTypeArguments.length) {
                                return;
                            }
                            for (int i = 0; i < parameterTypeArguments.length; i++) {
                                // 目标字段泛型是源字段泛型的父类或同类型的可以复制，反正不复制
                                if (!((Class<?>)parameterTypeArguments[i]).isAssignableFrom((Class<?>) returnTypeArguments[i])) {
                                    return;
                                }
                            }
                        }
                    } catch (RuntimeException e) {
                        e.printStackTrace();
                    }
                    // 属性复制
                    MethodInfo read = ReflectUtils.getMethodInfo(getter);
                    MethodInfo write = ReflectUtils.getMethodInfo(setter);
                    emitter1.load_local(targetLocal);
                    emitter1.load_local(sourceLocal);
                    emitter1.invoke(read);
                    emitter1.invoke(write);
                }
            });
            emitter1.return_value();
            // copy 方法结束
            emitter1.end_method();

            // 生成 convert 方法
            CodeEmitter emitter2 = classEmitter.begin_method(1, CONVERT, (Type[]) null);
            // new 一个目标实例
            emitter2.new_instance(targetType);
            emitter2.dup();
            emitter2.invoke_constructor(targetType);
            Local local = emitter2.make_local();
            emitter2.store_local(local);
            // 调用 copy 方法
            emitter2.load_this();
            emitter2.load_arg(0);
            emitter2.load_local(local);
            emitter2.invoke(emitter1.getMethodInfo());
            // 返回数据
            emitter2.load_local(local);
            emitter2.return_value();
            // convert 方法结束
            emitter2.end_method();
            // 类结束
            classEmitter.end_class();
        }

        protected Object firstInstance(Class type) throws InstantiationException, IllegalAccessException {
            return type.newInstance();
        }

        protected Object nextInstance(Object instance) {
            return instance;
        }
    }

    /**
     * key生成类
     */
    interface SoarBeanCopierKey {
        Object newInstance(String var1, String var2, String var3);
    }

}
